package com.novunda.platform.services;

import com.google.common.collect.Lists;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfPTable;
import com.novunda.platform.common.domain.LogCount;
import com.novunda.platform.common.exception.NovException;
import com.novunda.platform.common.handler.LogCallBack;
import com.novunda.platform.common.utils.*;
import com.novunda.platform.common.web.modal.Page;
import com.novunda.platform.entities.Log;
import com.novunda.platform.entities.ReportLog;
import com.novunda.platform.entities.Statistics;
import com.novunda.platform.entities.User;
import com.novunda.platform.entities.base.BaseEntity;
import com.novunda.platform.enums.ValidateExceptionEnum;
import com.novunda.platform.repositories.LogRepository;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

/**
 * ..
 * 审计记录服务
 *
 * @author yangg
 * @since 2016/9/12.
 */
@Service("logService")
@Transactional
public class LogService extends BaseService {

    @Autowired
    private LogRepository logRepository;
    @Autowired
    private UserService userService;
    @Autowired
    private StatisticsService statisticsService;

    @Autowired
    private Environment env;

    /**
     * 缓存名称
     */
    private static final String LOG_COUNT_KEY = "log_count";

    public Page<Log> find(Page<Log> page, String keyWord) {
        DetachedCriteria dc = logRepository.createDetachedCriteria();
        User user = UserUtils.getUser();
        // 如果当前用户不是admin
        if (!user.isAdmin()) {
            dc.add(Restrictions.eq("orgName", user.getCompany().getName()));
        }

        if (StringUtils.isNotEmpty(keyWord) && !"如：操作员 | 系统名称...".equals(keyWord)) {
            dc.add(Restrictions.or(Restrictions.like("description", "%" + keyWord + "%"),
                    Restrictions.like("type", "%" + keyWord + "%"),
                    Restrictions.like("name", "%" + keyWord + "%"),
                    Restrictions.like("system", "%" + keyWord + "%"),
                    Restrictions.like("result", "%" + keyWord + "%"),
                    Restrictions.like("ip", "%" + keyWord + "%"),
                    Restrictions.like("orgName", "%" + keyWord + "%"),
                    Restrictions.like("loginName", "%" + keyWord + "%")
            ));
        }
        dc.add(Restrictions.eq(BaseEntity.DEL_FLAG, BaseEntity.DEL_FLAG_NORMAL));
        dc.addOrder(Order.desc("id"));
        return logRepository.find(page, dc);
    }


    @Transactional(readOnly = false)
    public void save(Log log) {
        ValidateUtil.validateLog(log);
        logRepository.save(log);
    }

    /**
     * 获取总数
     *
     * @return 总数
     */
    public long count() {
        return logRepository.count();
    }


    /**
     * 生成导出文件
     *
     * @param startTime 开始时间
     * @param endTime   截止时间
     */
    public InputStream export(final Date startTime, final Date endTime) throws IOException {
        return (InputStream) handler.doHandler(new MyLogCallBack(startTime, endTime));

    }

    /**
     * 接收上报日志
     *
     * @param log 日志对象
     */
    public void saveReport(ReportLog log) {

        if (!beforeLog()) {
            return;
        }

        User user = validateLog(log);
        Log entyLog = new Log();

        entyLog.setDescription(log.getDescription());
        entyLog.setIp(log.getIp());
        entyLog.setSystem(log.getSystem());
        entyLog.setResult(log.getResult());
        entyLog.setName(log.getName());
        entyLog.setType(log.getType());
        entyLog.setUpdateDate(new Date(log.getUpdateDate()));
        entyLog.setCreateDate(new Date(log.getCreateDate()));
        entyLog.setUserName(user.getRealName());
        entyLog.setLoginName(user.getLoginName());
        logRepository.save(entyLog);

    }

    /**
     * 验证格式及用户
     *
     * @param log 审计对象
     * @return 用户
     */
    private User validateLog(ReportLog log) {
        ValidateUtil.validateReportLog(log);
        User user = userService.getUser(log.getUserId());
        if (user == null) {
            throw new NovException(ValidateExceptionEnum.INVALID_LOG_USER_NOTEXIST);
        }
        return user;
    }


    /**
     * 清空审计记录
     */
    public void clear() {
        handler.doHandler(new LogCallBack() {
            @Override
            public Object doHandler() {
                logRepository.deleteAll();
                CacheUtils.remove(LOG_COUNT_KEY);
                return null;
            }

            @Override
            public Log generate() {
                Log log = new Log();
                log.setType("删除");
                log.setName("清空审计记录");
                log.setDescription("清空审计记录");
                return log;
            }

            @Override
            public boolean forceLog() {
                return true;
            }
        });

    }

    /**
     * 是否需要记录
     *
     * @return 是否需要记录
     */
    private boolean beforeLog() {
        Statistics statistics = statisticsService.getStatistics();
        // 设置为不记录则不记录
        if (statistics.getRecordStatus() != 0) {
            return false;
        }

        if (CacheUtils.getObject(LOG_COUNT_KEY) == null) {
            CacheUtils.putObject(LOG_COUNT_KEY, new LogCount(count()));
        }
        LogCount logCount = (LogCount) CacheUtils.getObject(LOG_COUNT_KEY);
        //当前时间
        long lastTime = logCount.getLastTime();
        if (new Date().getTime() - lastTime >= Long.valueOf(env.getProperty("log.count.expire"))) {
            //缓存过期 更新缓存
            CacheUtils.putObject(LOG_COUNT_KEY, new LogCount(count()));
        }
        long countNum = logCount.getCountNum();

        long maxNum = statistics.getRecordNumber();
        //已有记录超过最大纪录 则 不记录
        return maxNum > countNum;
    }

    private class MyLogCallBack implements LogCallBack {
        private final Date startTime;
        private final Date endTime;

        public MyLogCallBack(Date startTime, Date endTime) {
            this.startTime = startTime;
            this.endTime = endTime;
        }

        @Override
        public Object doHandler() {
            try {
                User user = UserUtils.getUser();
                String orgName = "";
                // 如果当前用户不是admin
                if (!user.isAdmin()) {
                    orgName = user.getCompany().getName();
                }
                List<Log> logs;

                if (startTime == null && endTime == null) {
                    logs = StringUtil.isEmpty(orgName) ? logRepository.findAllList() : logRepository.findOrgAllList(orgName);
                } else if (startTime == null) {
                    logs = StringUtil.isEmpty(orgName) ? logRepository.findExportListBefore(endTime) : logRepository.findOrgExportListBefore(endTime, orgName);
                } else {
                    Calendar calendar = new GregorianCalendar();
                    calendar.setTime(endTime == null ? new Date() : endTime);
                    calendar.add(Calendar.DATE, 1);//把日期往后增加一天.整数往后推,负数往前移动
                    Date newend = calendar.getTime();   //这个时间就是日期往后推一天的结果
                    logs = StringUtil.isEmpty(orgName) ? logRepository.findExportList(startTime, newend == null ? new Date() : newend) : logRepository.findOrgExportList(startTime, newend == null ? new Date() : newend, orgName);
                }

                List<String> titleList = Lists.newArrayList();
                titleList.add("序列");
                titleList.add("日志类型");
                titleList.add("日志名称");
                titleList.add("系统名称");
                titleList.add("操作员");
                titleList.add("机构名称");
                titleList.add("操作IP");
                titleList.add("操作时间");
                titleList.add("事件结果");
                titleList.add("日志描述");

                float[] widths = {0.1f, 0.2f, 0.3f, 0.2f, 0.2f, 0.3f, 0.3f, 0.3f, 0.1f, 0.3f};
                PdfPTable table = CreatePdfUtil.createTable(widths, "日志列表：", titleList);
                int index = 1;
                for (Log log : logs) {
                    table.addCell(CreatePdfUtil.createContentCell(String.valueOf(index), 4));
                    table.addCell(CreatePdfUtil.createContentCell(StringUtils.isEmpty(log.getType()) ? "" : log.getType(), 6));
                    table.addCell(CreatePdfUtil.createContentCell(StringUtils.isEmpty(log.getName()) ? "" : log.getName(), 10));
                    table.addCell(CreatePdfUtil.createContentCell(StringUtils.isEmpty(log.getSystem()) ? "" : log.getSystem(), 6));
                    table.addCell(CreatePdfUtil.createContentCell(StringUtils.isEmpty(log.getLoginName()) ? "" :
                            log.getLoginName(), 10));

                    table.addCell(CreatePdfUtil.createContentCell(StringUtils.isEmpty(log.getOrgName()) ? "" :
                            log.getOrgName(), 10));

                    table.addCell(CreatePdfUtil.createContentCell(StringUtils.isEmpty(log.getIp()) ? "" : log.getIp(), 16));
                    //noinspection CallToDateToString
                    table.addCell(CreatePdfUtil.createContentCell(log.getCreateDate() == null ? "" :
                            log.getCreateDate().toString().substring(0, log.getCreateDate().toString().length() - 2), 20));
                    table.addCell(CreatePdfUtil.createContentCell(StringUtils.isEmpty(log.getResult()) ? "" : log.getResult(), 2));
                    table.addCell(CreatePdfUtil.createContentCell(StringUtils.isEmpty(log.getDescription()) ? "" : log.getDescription(), 10));
                    index++;
                }
                String fileName = "logs_report.pdf";
                File file = CreatePdfUtil.createDocument(table, "", fileName);
                return new FileInputStream(file);

            } catch (DocumentException | IOException e) {
                throw new NovException(e.getMessage(), e);
            }

        }

        @Override
        public Log generate() {
            Log log = new Log();
            log.setType("导出");
            log.setName("导出日志");
            log.setDescription("导出日志");
            return log;
        }

        @Override
        public boolean forceLog() {
            return false;
        }
    }

    /**
     * 用户登录时是否审计告警
     *
     * @return 是否告警
     */
    public boolean warn() {

        return count() >= statisticsService.getStatistics().getRecordAlarm();

    }

    /**
     * 选择时间段内是否有数据存在
     *
     * @param startTime 开始时间
     * @param endTime   截止时间
     * @return 是否存在数据
     */
    public boolean dataExist(Date startTime, Date endTime) {
        String orgName = "";
        User user = UserUtils.getUser();
        // 如果当前用户不是admin
        if (!user.isAdmin()) {
            orgName = user.getCompany().getName();
        }
        long count;
        if (startTime == null && endTime == null) {
            count = StringUtil.isEmpty(orgName) ? logRepository.findAllListCount() : logRepository.findOrgAllListCount(orgName);
        } else if (startTime == null) {
            count = StringUtil.isEmpty(orgName) ? logRepository.findExportListBeforeCount(endTime) :
                    logRepository.findOrgExportListBeforeCount(endTime, orgName);
        } else {
            Calendar calendar = new GregorianCalendar();
            calendar.setTime(endTime == null ? new Date() : endTime);
            calendar.add(Calendar.DATE, 1);//把日期往后增加一天.整数往后推,负数往前移动
            Date newend = calendar.getTime();   //这个时间就是日期往后推一天的结果
            count = StringUtil.isEmpty(orgName) ? logRepository.findExportListCount(startTime, newend == null ? new Date() : newend) :
                    logRepository.findOrgExportListCount(startTime, newend == null ? new Date() : newend, orgName);
        }
        return count > 0;
    }
}
